#include <linux/module.h>
#include <linux/init.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <linux/string.h>

#define YESNO_MAGIC 232535
#define LEVELS 5
int mydepth=5;
MODULE_LICENSE("GPL");
//static int mycount =2;

module_param(mydepth, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);

static struct inode *yesno_get_inode(struct super_block *, int);


static struct dentry *yesno_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
	/* TBD */
	printk("lookup denry name %s \n", dentry->d_name.name);
	if(dentry->d_inode)
		printk("lookup denry inode %s \n", dentry->d_inode->i_ino);
	struct inode *myinode;		
	printk("lookup parent inode %i\n", dir->i_ino);
	printk("lookup  inode depth %i\n", *((int*)dir->i_private));
	if(strcmp(dentry->d_name.name,"yes") == 0 && (mydepth > *((int*)dir->i_private)))
	{
		myinode = yesno_get_inode(dir->i_sb, ((dir->i_ino)*3)-1);
		*((int*)myinode->i_private)=*((int*)dir->i_private)+1;
		d_add(dentry, myinode);
		printk("lookup myinode %i depth %i \n", dentry->d_inode->i_ino, *((int*)myinode->i_private));
	}	
	if(strcmp(dentry->d_name.name,"no") == 0 && (mydepth > *((int*)dir->i_private)))
	{
		myinode = yesno_get_inode(dir->i_sb, ((dir->i_ino)*3));
		*((int*)myinode->i_private)=*((int*)dir->i_private)+1;
		d_add(dentry, myinode);
		printk("lookup myinode %i depth %i \n", dentry->d_inode->i_ino, *((int*)myinode->i_private));
	}	
	if(strcmp(dentry->d_name.name,"donno") == 0 && (mydepth > *((int*)dir->i_private)))
	{
		myinode = yesno_get_inode(dir->i_sb, ((dir->i_ino)*3+1));
		*((int*)myinode->i_private)=*((int*)dir->i_private)+1;
		d_add(dentry, myinode);
		printk("lookup myinode %i depth %i \n", dentry->d_inode->i_ino, *((int*)myinode->i_private));
	}	
	return NULL;

}



static const struct inode_operations yesno_dir_inode_operations = {
	.lookup		= yesno_lookup,
};

static int yesno_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
	/* TBD */
	return 0;
}

int rkfs_f_readdir( struct file *file, void *dirent, filldir_t filldir ) {
	int err;
	struct dentry *de = file->f_dentry;
	printk( "rkfs: file_operations.readdir called, dentry ino %i\n", de->d_inode->i_ino);
	if(file->f_pos > 0 )
		return 1;
	if(filldir(dirent, ".", 1, file->f_pos++, de->d_inode->i_ino, DT_DIR)||
		(filldir(dirent, "..", 2, file->f_pos++, de->d_parent->d_inode->i_ino, DT_DIR)))
	        return 0;
	if(filldir(dirent, "yes", 3, file->f_pos++, ((de->d_inode->i_ino)*3-1), DT_REG ))
		return 0;
	if(filldir(dirent, "no", 2, file->f_pos++, ((de->d_inode->i_ino)*3), DT_REG ))
        	return 0;
	if(filldir(dirent, "donno", 5, file->f_pos++, ((de->d_inode->i_ino)*3+1), DT_REG ))
        	return 0;
    return 3;
	
}

static const struct file_operations yesno_dir_operations = {
	.readdir = 	rkfs_f_readdir,
	.owner =	THIS_MODULE,
	.read = generic_read_dir,
};


struct inode *yesno_get_inode(struct super_block *sb, int num)
{
	struct inode *inode = new_inode(sb);

	if (inode) {
		inode->i_mode = S_IFDIR | 0555;
		inode->i_uid = 0;
		inode->i_gid = 0;
		inode->i_blocks = 0;
		inode->i_op = &yesno_dir_inode_operations;
		inode->i_fop = &yesno_dir_operations;
		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
		inc_nlink(inode);
		inode->i_ino = num;
		inode->i_private=(int*)kmalloc(sizeof(int), GFP_KERNEL);
		*((int*)inode->i_private)=0;
	}
	return inode;
}

static struct super_operations yesno_ops = {
	.statfs 	= simple_statfs,
};

static int yesno_fill_super(struct super_block *sb, void *data, int silent)
{
	struct inode *inode;
	struct dentry *root;

	sb->s_maxbytes = MAX_LFS_FILESIZE;
	sb->s_blocksize = PAGE_CACHE_SIZE;
	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
	sb->s_magic = YESNO_MAGIC;
	sb->s_op = &yesno_ops;
	sb->s_time_gran = 1;
	inode = yesno_get_inode(sb, 1);
	if (!inode)
		return -ENOMEM;
	root = d_alloc_root(inode);
	if (!root) {
		iput(inode);
		return -ENOMEM;
	}
	sb->s_root = root;
	return 0;
}


/*int yesno_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
	return get_sb_nodev(fs_type, flags, data, yesno_fill_super, mnt);
}*/


static struct dentry *mymount(struct file_system_type *fs_type, int flags,
	const char *dev_name, void *data)
{
	return mount_nodev(fs_type, flags, data, yesno_fill_super);
}


static struct file_system_type yesno_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "yesno",
	//.get_sb		= yesno_get_sb,
	.kill_sb	= kill_anon_super,
	.mount		= mymount,
};






static int __init init_dfs(void)
{
	int res = register_filesystem(&yesno_fs_type);
	printk("reg filesys res %i\n",res);
	return res;
}

static void __exit exit_dfs(void)
{
	unregister_filesystem(&yesno_fs_type);
}

module_init(init_dfs)
module_exit(exit_dfs)



